/*
 * @file camera.cpp
 * @Brief a camera class for OpenGL.
 * @author Fei Zhu
 * 
 * This file is part of Physika, a versatile physics simulation library.
 * Copyright (C) 2013- Physika Group.
 *
 * This Source Code Form is subject to the terms of the GNU General Public License v2.0. 
 * If a copy of the GPL was not distributed with this file, you can obtain one at:
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 */

#include <Display/camera.h>
#include <Display/quaternion.h>
using namespace glm;

namespace PhysLeo{

Camera::Camera():
    camera_position_(vec3(0)),
    camera_up_(vec3(0,1,0)),
    focus_position_(vec3(0,0,-1)),
    fov_(45.0f),view_aspect_(640.0f/480.0f),
    near_clip_(1.0f),
    far_clip_(100.0f)
{
}

Camera::Camera(const vec3 &camera_position, const vec3 &camera_up, const vec3 &focus_position, float field_of_view, float view_aspect, float near_clip, float far_clip): 
    camera_position_(camera_position),
    camera_up_(camera_up),
    focus_position_(focus_position),
    fov_(field_of_view),
    view_aspect_(view_aspect),
    near_clip_(near_clip),
    far_clip_(far_clip)
{
}

void Camera::orbitUp(float rad)
{
    orbitDown(-rad);
}

void Camera::orbitDown(float rad)
{
    //first update camera position
    auto camera_direction = focus_position_ - camera_position_;
    const auto axis = glm::normalize(glm::cross(camera_direction, camera_up_));
    const  Quaternion quat(axis,rad);
    camera_position_ = quat.rotate(camera_position_);
    //then update up direction
    camera_direction = focus_position_ - camera_position_;
    camera_up_ = glm::normalize(glm::cross(axis, camera_direction));
}

void Camera::orbitLeft(float rad)
{
    orbitRight(-rad);
}

void Camera::orbitRight(float rad)
{
    Quaternion quat(camera_up_,rad);
    camera_position_ = quat.rotate(camera_position_);
}

void Camera::zoomIn(float dist)
{
	const auto camera_direction = glm::normalize(focus_position_ - camera_position_);
    camera_position_ += dist * camera_direction;
}

void Camera::zoomOut(float dist)
{
    zoomIn(-dist);
}

void Camera::yaw(float rad)
{
	Quaternion quat(camera_up_, rad);
    auto camera_direction = focus_position_ - camera_position_;
    camera_direction = quat.rotate(camera_direction);
    focus_position_ = camera_position_ + camera_direction;
}

void Camera::pitch(float rad)
{
    auto camera_direction = focus_position_ - camera_position_;
	const auto axis = normalize(cross(camera_direction, camera_up_));
	const Quaternion quat(axis, rad);
    camera_direction = quat.rotate(camera_direction);
    focus_position_ = camera_position_ + camera_direction;
}

void Camera::roll(float rad)
{
	const auto camera_direction = normalize(focus_position_ - camera_position_);
	const Quaternion quat(camera_direction, rad);
    camera_up_ = quat.rotate(camera_up_);
}

void Camera::translateUp(float dist)
{
	camera_position_ += dist*camera_up_;
    focus_position_ += dist*camera_up_;
}

void Camera::translateDown(float dist)
{
    translateUp(-dist);
}

void Camera::translateRight(float dist)
{
    const auto camera_direction = focus_position_ - camera_position_;
    const auto axis = normalize(cross(camera_direction, camera_up_));
    camera_position_ += dist * axis;
    focus_position_ += dist * axis;
}

void Camera::translateLeft(float dist)
{
    translateRight(-dist);
}

} //end of namespace Physika
